// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package walk

import (
	"encoding/binary"
	"go/constant"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/reflectdata"
	"cmd/compile/internal/ssagen"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/src"
	"cmd/internal/sys"
)

// walkConv遍历一个OCONV或OCONVNOP（但不是OCONVIFACE）节点。
func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	n.X = walkExpr(n.X, init)
	if n.Op() == ir.OCONVNOP && n.Type() == n.X.Type() {
		return n.X
	}
	if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) {
		if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { // uintptr不安全。指针
			return walkCheckPtrArithmetic(n, init)
		}
	}
	param, result := rtconvfn(n.X.Type(), n.Type())
	if param == types.Txxx {
		return n
	}
	fn := types.BasicTypeNames[param] + "to" + types.BasicTypeNames[result]
	return typecheck.Conv(mkcall(fn, types.Types[result], init, typecheck.Conv(n.X, types.Types[param])), n.Type())
}

// walkConvInterface遍历OConvFace节点。
func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node {

	n.X = walkExpr(n.X, init)

	fromType := n.X.Type()
	toType := n.Type()
	if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) {
		// 跳过未命名函数（func（））
		reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym)
	}

	if !fromType.IsInterface() {
		var typeWord ir.Node
		if toType.IsEmptyInterface() {
			typeWord = reflectdata.TypePtr(fromType)
		} else {
			typeWord = reflectdata.ITabAddr(fromType, toType)
		}
		l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone))
		l.SetType(toType)
		l.SetTypecheck(n.Typecheck())
		return l
	}
	if fromType.IsEmptyInterface() {
		base.Fatalf("OCONVIFACE can't operate on an empty interface")
	}

	// 计算输入接口。抓住它的部分。
	c := typecheck.Temp(fromType)
	init.Append(ir.NewAssignStmt(base.Pos, c, n.X))

	itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, c)
	itab.SetType(types.Types[types.TUINTPTR].PtrTo())
	itab.SetTypecheck(1)
	data := ir.NewUnaryExpr(n.Pos(), ir.OIDATA, c)
	data.SetType(types.Types[types.TUINT8].PtrTo()) // 类型是泛型指针——我们只是在传递它。
	data.SetTypecheck(1)

	var typeWord ir.Node
	if toType.IsEmptyInterface() {
		// 实现接口到空接口的转换。
		// res=itab 
		// if res！=nil{
		// res=res.type 
		// }
		typeWord = typecheck.Temp(types.NewPtr(types.Types[types.TUINT8]))
		init.Append(ir.NewAssignStmt(base.Pos, typeWord, itab))
		nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, typeWord, typecheck.NodNil())), nil, nil)
		nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, typeWord, itabType(typeWord))}
		init.Append(nif)
	} else {
		// 必须转换I2I（更具体到不太具体的接口）。
		// res=convI2I（toType，itab）
		fn := typecheck.LookupRuntime("convI2I")
		types.CalcSize(fn.Type())
		call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
		call.Args = []ir.Node{reflectdata.TypePtr(toType), itab}
		typeWord = walkExpr(typecheck.Expr(call), init)
	}

	// 生成结果。
	// e=iface{typeWord，data}
	e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, data)
	e.SetType(toType) // 手动分配类型，typecheck无法理解OEFACE。
	e.SetTypecheck(1)
	return e
}

// 返回接口中用来表示n的数据字（第二个字）。
// n不能是接口类型。
// esc描述结果是否逃逸。
func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node {
	fromType := n.Type()

	// 如果它是指针，那么它就是它自己的表示。
	if types.IsDirectIface(fromType) {
		return n
	}

	// 尝试一系列案例以避免分配。
	var value ir.Node
	switch {
	case fromType.Size() == 0:
		// n为零大小。使用zerobase。
		cheapExpr(n, init) // 评估n的副作用。见19246期。
		value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
	case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()):
		// n是布尔/字节。在小尾数
		// 上使用staticuint64s[n*8]，在大尾数上使用staticuint64s[n*8+7]。
		n = cheapExpr(n, init)
		// byteindex将n加宽，这样乘法就不会溢出。
		index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(3))
		if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
			index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(7))
		}
		// 实际类型是[256]uint64，但我们使用[256*8]uint8，因此我们可以处理单个字节。
		staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, types.NewArray(types.Types[types.TUINT8], 256*8))
		xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
		xe.SetBounded(true)
		value = xe
	case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
		// n是一个只读全局变量；直接使用它。
		value = n
	case !escapes && fromType.Size() <= 1024:
		// n无法逃脱。使用初始化为n的临时堆栈。
		value = typecheck.Temp(fromType)
		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n)))
	}
	if value != nil {
		// 接口数据字为&value。
		return typecheck.Expr(typecheck.NodAddr(value))
	}

	// 分配时间到了。我们将为此调用运行时。
	fnname, argType, needsaddr := dataWordFuncName(fromType)
	fn := typecheck.LookupRuntime(fnname)

	var args []ir.Node
	if needsaddr {
		// 通过引用传递大或未知大小的类型。
		// Orderexpr安排n作为所有
		// 它可以看到的转换的临时值。order无法看到接口
		// 与非接口的比较，尤其是在打开接口值
		// 与非接口情况下。stmt，所以我们
		// 必须在这里分配一个临时工。
		if !ir.IsAddressable(n) {
			n = copyExpr(n, fromType, init)
		}
		fn = typecheck.SubstArgTypes(fn, fromType)
		args = []ir.Node{reflectdata.TypePtr(fromType), typecheck.NodAddr(n)}
	} else {
		// 使用一个特殊的转换例程，该例程的类型为
		// 通过值而不是指针进行转换。
		var arg ir.Node
		switch {
		case fromType == argType:
			// 已经在正确的类型中，无需执行任何操作
			arg = n
		case fromType.Kind() == argType.Kind(),
			fromType.IsPtrShaped() && argType.IsPtrShaped():
			// 可以直接转换（例如，命名类型到基础类型，或一个指针指向另一个）
			// TODO:永远不会发生，因为指针是直接的？
			arg = ir.NewConvExpr(pos, ir.OCONVNOP, argType, n)
		case fromType.IsInteger() && argType.IsInteger():
			// 可以直接转换（例如int32到uint32）
			arg = ir.NewConvExpr(pos, ir.OCONV, argType, n)
		default:
			// 不安全的强制转换内存
			arg = copyExpr(n, fromType, init)
			var addr ir.Node = typecheck.NodAddr(arg)
			addr = ir.NewConvExpr(pos, ir.OCONVNOP, argType.PtrTo(), addr)
			arg = ir.NewStarExpr(pos, addr)
			arg.SetType(argType)
		}
		args = []ir.Node{arg}
	}
	call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
	call.Args = args
	return safeExpr(walkExpr(typecheck.Expr(call), init), init)
}

// walkConvIData遍历OCONVIDATA节点。
func walkConvIData(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	n.X = walkExpr(n.X, init)
	return dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone)
}

// WalkBytesRunestString遍历OBYTES2STR或ORUNES2STR节点。
func walkBytesRunesToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	a := typecheck.NodNil()
	if n.Esc() == ir.EscNone {
		// 为堆栈上的字符串创建临时缓冲区。
		a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
	}
	if n.Op() == ir.ORUNES2STR {
		// slicerunetostring（*[32]字节，[]符文）string 
		return mkcall("slicerunetostring", n.Type(), init, a, n.X)
	}
	// slicebytetostring（*[32]字节，ptr*字节，n int）string 
	n.X = cheapExpr(n.X, init)
	ptr, len := backingArrayPtrLen(n.X)
	return mkcall("slicebytetostring", n.Type(), init, a, ptr, len)
}

// walkBytesToStringTemp遍历OBYTES2STRTMP节点。
func walkBytesToStringTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	n.X = walkExpr(n.X, init)
	if !base.Flag.Cfg.Instrumenting {
		// 让后端直接处理OBYTES2STRTMP 
		// 以避免对SliceByteToStringMP的函数调用。
		return n
	}
	// slicebytetostringmp（ptr*字节，n int）字符串
	n.X = cheapExpr(n.X, init)
	ptr, len := backingArrayPtrLen(n.X)
	return mkcall("slicebytetostringtmp", n.Type(), init, ptr, len)
}

// walkRuneToString遍历ORUNESTR节点。
func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	a := typecheck.NodNil()
	if n.Esc() == ir.EscNone {
		a = stackBufAddr(4, types.Types[types.TUINT8])
	}
	// intstring（*[4]字节，符文）
	return mkcall("intstring", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TINT64]))
}

// walkStringToBytes走一个ost2bytes节点。
func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	s := n.X
	if ir.IsConst(s, constant.String) {
		sc := ir.StringVal(s)

		// 分配一个大小合适的[n]字节。
		t := types.NewArray(types.Types[types.TUINT8], int64(len(sc)))
		var a ir.Node
		if n.Esc() == ir.EscNone && len(sc) <= int(ir.MaxImplicitStackVarSize) {
			a = stackBufAddr(t.NumElem(), t.Elem())
		} else {
			types.CalcSize(t)
			a = ir.NewUnaryExpr(base.Pos, ir.ONEW, nil)
			a.SetType(types.NewPtr(t))
			a.SetTypecheck(1)
			a.MarkNonNil()
		}
		p := typecheck.Temp(t.PtrTo()) // /*[n]字节
		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, p, a)))

		// 将静态字符串数据复制到[n]字节。
		if len(sc) > 0 {
			as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), t.PtrTo())))
			appendWalkStmt(init, as)
		}

		// 将[n]字节切片为[]字节。
		slice := ir.NewSliceExpr(n.Pos(), ir.OSLICEARR, p, nil, nil, nil)
		slice.SetType(n.Type())
		slice.SetTypecheck(1)
		return walkExpr(slice, init)
	}

	a := typecheck.NodNil()
	if n.Esc() == ir.EscNone {
		// 为堆栈上的切片创建临时缓冲区。
		a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
	}
	// stringtoslicebyte（*32[byte]，string）[]字节
	return mkcall("stringtoslicebyte", n.Type(), init, a, typecheck.Conv(s, types.Types[types.TSTRING]))
}

// walkStringToBytesTemp在OST2ByteSTMP节点上行走。
func walkStringToBytesTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	// /[]字节（字符串）转换，创建一个切片
	// 指的是实际的字符串字节。
	// 此转换稍后由后端处理，
	// 仅用于内部编译器优化
	// 知道切片不会发生变异。
	// 今天唯一的这种情况是：
	// 对于i，c:=range[]字节（字符串）
	n.X = walkExpr(n.X, init)
	return n
}

// walkStringToRunes走一个ost2Runes节点。
func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	a := typecheck.NodNil()
	if n.Esc() == ir.EscNone {
		// 为堆栈上的切片创建临时缓冲区。
		a = stackBufAddr(tmpstringbufsize, types.Types[types.TINT32])
	}
	// stringtoslicerune（*[32]符文，字符串）[]符文
	return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING]))
}

// dataWordFuncName返回用于将类型为“
// 的值转换为接口的数据字的函数名。
// argType是需要强制参数的类型。
// needsaddr报告是应该传递值（needaddr==false）还是应该传递其地址（needsaddr==true）。
func dataWordFuncName(from *types.Type) (fnname string, argType *types.Type, needsaddr bool) {
	if from.IsInterface() {
		base.Fatalf("can only handle non-interfaces")
	}
	switch {
	case from.Size() == 2 && uint8(from.Alignment()) == 2:
		return "convT16", types.Types[types.TUINT16], false
	case from.Size() == 4 && uint8(from.Alignment()) == 4 && !from.HasPointers():
		return "convT32", types.Types[types.TUINT32], false
	case from.Size() == 8 && uint8(from.Alignment()) == uint8(types.Types[types.TUINT64].Alignment()) && !from.HasPointers():
		return "convT64", types.Types[types.TUINT64], false
	}
	if sc := from.SoleComponent(); sc != nil {
		switch {
		case sc.IsString():
			return "convTstring", types.Types[types.TSTRING], false
		case sc.IsSlice():
			return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false // 元素类型无关紧要
		}
	}

	if from.HasPointers() {
		return "convT", types.Types[types.TUNSAFEPTR], true
	}
	return "convTnoptr", types.Types[types.TUNSAFEPTR], true
}

// rtconvfn返回参数和结果类型，这些参数和结果类型将由
// 运行时函数用于从类型src转换为类型dst。运行时函数
// name可以从返回类型的名称派生。
// 
// 如果不需要这样的函数，则返回（Txxx，Txxx）。
func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
	if ssagen.Arch.SoftFloat {
		return types.Txxx, types.Txxx
	}

	switch ssagen.Arch.LinkArch.Family {
	case sys.ARM, sys.MIPS:
		if src.IsFloat() {
			switch dst.Kind() {
			case types.TINT64, types.TUINT64:
				return types.TFLOAT64, dst.Kind()
			}
		}
		if dst.IsFloat() {
			switch src.Kind() {
			case types.TINT64, types.TUINT64:
				return src.Kind(), dst.Kind()
			}
		}

	case sys.I386:
		if src.IsFloat() {
			switch dst.Kind() {
			case types.TINT64, types.TUINT64:
				return types.TFLOAT64, dst.Kind()
			case types.TUINT32, types.TUINT, types.TUINTPTR:
				return types.TFLOAT64, types.TUINT32
			}
		}
		if dst.IsFloat() {
			switch src.Kind() {
			case types.TINT64, types.TUINT64:
				return src.Kind(), dst.Kind()
			case types.TUINT32, types.TUINT, types.TUINTPTR:
				return types.TUINT32, types.TFLOAT64
			}
		}
	}
	return types.Txxx, types.Txxx
}

// byteindex将字节大小的n转换为用于索引到数组中的整数。
// 我们不能使用conv，因为我们允许将bool转换为int，这在用户代码中是禁止的。
func byteindex(n ir.Node) ir.Node {
	// 我们不能直接从bool转换为int。
	// 虽然可以将int8转换为int，但会产生错误的负值结果。
	// 将值重新解释为无符号字节可以解决这两种情况。
	if !types.Identical(n.Type(), types.Types[types.TUINT8]) {
		n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
		n.SetType(types.Types[types.TUINT8])
		n.SetTypecheck(1)
	}
	n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
	n.SetType(types.Types[types.TINT])
	n.SetTypecheck(1)
	return n
}

func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
	// 下面调用cheapExpr（n，init）会导致对
	// walkExpr的递归调用，这又把我们带回到这里。使用n.Checkptr来
	// 防止无限循环。
	if n.CheckPtr() {
		return n
	}
	n.SetCheckPtr(true)
	defer n.SetCheckPtr(false)

	// TODO（mdempsky）：更加严格。我们只需要豁免
	// 反射。价值指针和反射。价值UnsafeAddr。
	switch n.X.Op() {
	case ir.OCALLMETH:
		base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
	case ir.OCALLFUNC, ir.OCALLINTER:
		return n
	}

	if n.X.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(n.X) {
		return n
	}

	// 发现原始不安全。此
	// 算术表达式中涉及的指针操作数。
	// 
	// “以这种方式对
	// /指针的偏移量进行加法和减法都是有效的。通常为了对齐，使用&^对
	// /指针进行四舍五入也是有效的。”
	var originals []ir.Node
	var walk func(n ir.Node)
	walk = func(n ir.Node) {
		switch n.Op() {
		case ir.OADD:
			n := n.(*ir.BinaryExpr)
			walk(n.X)
			walk(n.Y)
		case ir.OSUB, ir.OANDNOT:
			n := n.(*ir.BinaryExpr)
			walk(n.X)
		case ir.OCONVNOP:
			n := n.(*ir.ConvExpr)
			if n.X.Type().IsUnsafePtr() {
				n.X = cheapExpr(n.X, init)
				originals = append(originals, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]))
			}
		}
	}
	walk(n.X)

	cheap := cheapExpr(n, init)

	slice := typecheck.MakeDotArgs(base.Pos, types.NewSlice(types.Types[types.TUNSAFEPTR]), originals)
	slice.SetEsc(ir.EscNone)

	init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice))
	// TODO（khr）：将切片的备份存储标记为死。这将允许我们重新使用
	// 备份存储区，以便多次调用checkptrArithmetic。

	return cheap
}
